package gragh;

import java.io.Serializable;
import java.util.ArrayList;
import constraint.*;
import database.constraint.ListBusinessConstraint;

public class Equalivation implements Serializable {
    
    /**
     * 
     */
    private static final long serialVersionUID = 1;
    public ArrayList<ENode> lsNode;
    
    public Equalivation() {
        lsNode = new ArrayList<ENode>();
    }

    public void addNode(ENode node) {
        lsNode.add(node);
    }
    
    public ArrayList<ENode> getLsNode() {
        return lsNode;
    }
    
    /**
     * Ham tao ra list Equalivation tu list bussiness constraint
     * @param lsBConstraint
     * @return
     */
    public static ArrayList<Equalivation> creatEqualivationMap(ListBusinessConstraint lsBConstraint) {
        // TODO viet lai clone
        ListBusinessConstraint lsBC = (ListBusinessConstraint) lsBConstraint.clone();
        ArrayList<Equalivation> equalivationMap = new ArrayList<Equalivation>();
        boolean isMore = false;
        int i = 0;
        ENode n1 = null;
        ENode n2 = null;
        
        while (lsBC.size() > 0) {
            Equalivation e = new Equalivation();
            constraint.Constraint bc = lsBC.get(0);
            
            n1 = takeENode_1(bc);
            n2 = takeENode_2(bc);
            
            n1.directLink = n2;
            n2.directLink = n1;
            
            e.addNode(n1);
            e.addNode(n2);
            lsBC.remove(0);
            
            // xu ly tham chieu de quy
            if (n1.special == 1) {
                equalivationMap.add(e);
                continue;
            }
            do {
                isMore = false;
                i = 0;
                while (i < lsBC.size()) {
                    bc = lsBC.get(i);
                    n1 = takeENode_1(bc);
                    n2 = takeENode_2(bc);
                    
                    // xu ly tham chieu de quy
                    if (n1.special == 1) {
                        i++;
                        continue;
                    }
                    
                      // co le la so sanh kieu nay
                    if (isEContainsN(e, n1)) {
                        if (!e.lsNode.contains(n2)) {
                            n2.directLink = n1.directLink;
                            e.addNode(n2);
                            isMore = true;
                        }
                        lsBC.remove(i);
                    } else if (isEContainsN(e, n2)) {
                        if (!e.lsNode.contains(n1)) {
                            n1.directLink = n2.directLink;
                            e.addNode(n1);
                            isMore = true;
                        }
                        lsBC.remove(i);
                    } else {
                        i++;
                    }
                }
            } while (isMore);
            
            equalivationMap.add(e);
        }
        
        return equalivationMap;
    }
    
    /**
     * Kiem tra xem doi tuong Equalivation e co chua doi tuong ENode n hay ko.
     * Kiem tra bang cach so sanh gia tri strValue cua ENode.
     * @param e
     * @param n
     * @return
     */
    private static boolean isEContainsN(Equalivation e, ENode n) {
        boolean result = false;
        
        int i = 0;
        while(!result && i < e.lsNode.size()) {
//          result = n.isEqual(e.lsNode.get(i));
            if (n.type == e.lsNode.get(i).type) {
                if(n.strValue.equals(e.lsNode.get(i).strValue)) {
                    n.directLink = e.lsNode.get(i);
                    result = true;
                }
            }
            i++;
        }
        
        return result;
    }
    
    /**
     * Tao ra doi tuong ENode tu ve phai cua 1 bussiness constraint.
     * @param c
     * @return
     */
    private static ENode takeENode_1(constraint.Constraint c) {
        ENode node = null;
        StringBuffer sb = new StringBuffer();
        
        switch (c.type) {
        case 2:
        case 4:
            Constraint_v2 c2 = (Constraint_v2) c;
            node = createENodeFromElement(c2.left);
            if (c2.isFuncLeft) {
                node.type = 11;
            } else {
                node.type = 10;
            }
            
            // cap nhat strViewValue
            if (c2.isFuncLeft) {
                sb.append("func(");
            }
            
            for (Element e : c2.left) {
                sb.append(e.sPrintMyself());
            }
            
            if (c2.isFuncLeft) {
                sb.append(")");
            }
            node.strViewValue = sb.toString();
            
            // xu ly tham chieu de quy
            if (c.type == 4) {
                Constraint_v4 c4 = (Constraint_v4) c;
                String tmp = c4.join.get(0).table1;
                tmp = tmp.substring(tmp.length()-1, tmp.length());
                if (tmp.equals("]")) {
                    node.special = 1;
                }
            }
            break;
        case 3:
            Constraint_v3 c3 = (Constraint_v3) c;
            node = createENodeFromGroup(c3.leftGroup);
            node.type = 20;
            
            // cap nhat strViewValue
            sb.append(c3.leftGroup.sPrintMyself());
            node.strViewValue = sb.toString();
            break;
        case 5:
            Constraint_v5 c5 = (Constraint_v5) c;
            node = createENodeFromElement(c5.elements);
            if (c5.isFuncElements) {
                node.type = 11;
            } else {
                node.type = 10;
            }
            
            // cap nhat strViewValue
            if (c5.isFuncElements) {
                sb.append("func(");
            }
            
            for (Element e : c5.elements) {
                sb.append(e.sPrintMyself());
            }
            
            if (c5.isFuncElements) {
                sb.append(")");
            }
            node.strViewValue = sb.toString();
            break;
        case 6:
            Constraint_v6 c6 = (Constraint_v6) c;
            node = createENodeFromGroup(c6.leftGroup);
            node.type = 20;
            
         // cap nhat strViewValue
            sb.append(c6.leftGroup.sPrintMyself());
            node.strViewValue = sb.toString();
            break;
        }
        
        node.isLeft = true;
        node.constraint = c;
        return node;
    }
    
    /**
     * Tao ra doi tuong ENode tu ve phai cua 1 bussiness constraint.
     * @param c
     * @return
     */
    private static ENode takeENode_2(constraint.Constraint c) {
        ENode node = null;
        StringBuffer sb = new StringBuffer();
        
        switch (c.type) {
        case 2:
        case 4:
            Constraint_v2 c2 = (Constraint_v2) c;
            node = createENodeFromElement(c2.right);
            if (c2.isFuncRight) {
                node.type = 11;
            } else {
                node.type = 10;
            }
            
            // cap nhat strViewValue
            if (c2.isFuncRight) {
                sb.append("func(");
            }
            
            for (Element e : c2.right) {
                sb.append(e.sPrintMyself());
            }
            
            if (c2.isFuncRight) {
                sb.append(")");
            }
            node.strViewValue = sb.toString();
            
            // xu ly tham chieu de quy
            if (c.type == 4) {
                Constraint_v4 c4 = (Constraint_v4) c;
                String tmp = c4.join.get(0).table1;
                tmp = tmp.substring(tmp.length()-1, tmp.length());
                if (tmp.equals("]")) {
                    node.special = 1;
                }
            }
            break;
        case 3:
            Constraint_v3 c3 = (Constraint_v3) c;
            node = new ENode();
            node.add(c3.rightElement.index);
            node.constant = c3.rightElement.idName;
            node.type = 90;
            
            // cap nhat strViewValue
            sb.append(c3.rightElement.sPrintMyself());
            node.strViewValue = sb.toString();
            break;
        case 5:
            Constraint_v5 c5 = (Constraint_v5) c;
            node = createENodeFromGroup(c5.group);
            node.type = 20;
            
            // cap nhat strViewValue
            sb.append(c5.group.sPrintMyself());
            node.strViewValue = sb.toString();
            break;
        case 6:
            Constraint_v6 c6 = (Constraint_v6) c;
            node = createENodeFromGroup(c6.rightGroup);
            node.type = 20;
            
            // cap nhat strViewValue
            sb.append(c6.rightGroup.sPrintMyself());
            node.strViewValue = sb.toString();
            break;
        }
        
        node.isLeft = false;
        node.constraint = c;
        return node;
    }
    
    /**
     * Tao ra doi tuong ENode tu doi tuong list Element.
     * @param lsEle
     * @return
     */
    private static ENode createENodeFromElement(ArrayList<Element> lsEle) {
        ENode node = new ENode();
        String tmp = "";
        
        for (Element e : lsEle) {
            node.add(e.index);
            node.func.add(e.func);
            if (e.index == -1) {
                node.constant = e.idName;
                tmp = e.idName;
            } else {
                tmp += e.index + e.func + "_";
            }
        }
        
        node .strValue = tmp;
        
        return node;
    }
    
    /**
     * Tao ra doi tuong ENode tu doi tuong Group.
     * @param g
     * @return
     */
    private static ENode createENodeFromGroup(Group g) {
        ENode node = new ENode();
        String tmp = "";
        node.type = 20;

        if (g.isFunc) {
            tmp += "gf_";
        }
        
        for (constraint.SubGroup sg : g.subGroups) {
            tmp += sg.groupName;
            if(sg.isFunc) {
                tmp += "sf_";
            }
            for (Element e : sg.elements) {
                tmp += e.index + "_";
            }
        }

        for (Integer inte : g.indexTables) {
            node.add(inte);
        }
          // neu co join, groupLeader se dc update sau
          // cu the la trong ham Redundancy.RedundancyAnalysis(ArrayList<Equalivation>, ArrayList<Table>, ArrayList<Way>)
        if (g.join.size() == 0) {
            node.groupLeader.add(node.get(0));
        }
        node.strValue = tmp;
        
        return node;
    }

}